/**
 * @file config.c
 * @brief functions for dealing with config files
 * @author Scott Moyers
 * @date 2010
 */

#include "config.h"
#include "selection.h"
#include "crossover.h"
#include "mutate.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

extern SelectionFunc selection_functions[SEL_FUNCS];
extern MutationFunc mutation_functions[MUT_FUNCS];
extern CrossoverFunc crossover_functions[CROSS_FUNCS];

#define BUF_SIZE 1024
static char buf[BUF_SIZE];

enum Command {
	NONE = -1,
	TRUTHTABLE = 0,
	MAXGATES,
	SIZE,
	GENERATIONS,
	SELECTION,
	MUTATION,
	CROSSOVER,
	MAXMUTATIONS,
	CROSSOVERPROB,
	KEEPRATIO,
	BREEDRATIO,
	NUM_COMMANDS
};

static const char *commands[NUM_COMMANDS] = {
	"truthtable",
	"maxgates",
	"size",
	"generations",
	"selection",
	"mutation",
	"crossover",
	"maxmutations",
	"crossoverprob",
	"keepratio",
	"breedratio"
};

static int is_whitespace(char c)
{
	if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
		return 1;
	}
	return 0;
}

static char *trim_whitespace(char *str)
{
	char *end;
	while(is_whitespace(*str)) {
		str++;
	}
	end = str + strlen(str) - 1;
	while(end > str && is_whitespace(*end)) {
		end--;
	}
	*(end + 1) = '\0';
	return str;
}

int load_config_file(struct Population *pop, const char *filename)
{
	int c;
	int command = NONE;
	int pos = 0;
	FILE *fp = fopen(filename, "r");
	if (fp == NULL) {
		fprintf(stderr, "Unable to open file %s for reading\n", filename);
		exit(1);
	}

	while ((c = fgetc(fp)) != EOF) {
		buf[pos] = c;
		if (is_whitespace(buf[pos])) {
			int i;
			char *str;
			buf[pos] = '\0';
			str = trim_whitespace(buf);
			if (strlen(str) != 0) {
				switch(command) {
				case NONE:
					for(i = 0; i < NUM_COMMANDS; i++) {
						if (!strcmp(str, commands[i])) {
							command = i;
						}
					}
					break;
				case TRUTHTABLE:
					pop->truth_table = new_truth_table_from_file(str);
					if (pop->truth_table == NULL) {
						fclose(fp);
						return 1;
					}
					command = NONE;
					break;
				case MAXGATES:
					{
						int val = atoi(str);
						if (val <= 0) {
							fprintf(stderr, "maxgates must be > 0: %s\n", str);
							fclose(fp);
							return 1;
						}
						pop->max_ckt_size = (unsigned) val;
					}
					command = NONE;
					break;
				case SIZE:
					{
						int val = atoi(str);
						if (val <= 0) {
							fprintf(stderr, "size must be > 0: %s\n", str);
							fclose(fp);
							return 1;
						}
						pop->size = (unsigned) val;
					}
					command = NONE;
					break;
				case GENERATIONS:
					pop->max_generations = atoi(str);
					command = NONE;
					break;
				case SELECTION:
					if (!strcmp(str, "all")) {
						add_selection_funcs(pop, selection_functions, MUT_FUNCS);
					} else {
						SelectionFunc s = get_selection_func(str);
						if (s != NULL) {
							add_selection_func(pop, s);
						} else {
							fprintf(stderr, "Unrecognised selection function: %s\n", str);
							fclose(fp);
							return 1;
						}
					}
					command = NONE;
					break;
				case MUTATION:
					if (!strcmp(str, "all")) {
						add_mutation_funcs(pop, mutation_functions, MUT_FUNCS);
					} else {
						MutationFunc m = get_mutation_func(str);
						if (m != NULL) {
							add_mutation_func(pop, m);
						} else {
							fprintf(stderr, "Unrecognised mutation function: %s\n", str);
							fclose(fp);
							return 1;
						}
					}
					command = NONE;
					break;
				case CROSSOVER:
					if (!strcmp(str, "all")) {
						add_crossover_funcs(pop, crossover_functions, CROSS_FUNCS);
					} else {
						CrossoverFunc c = get_crossover_func(str);
						if (c != NULL) {
							add_crossover_func(pop, c);
						} else {
							fprintf(stderr, "Unrecognised crossover function: %s\n", str);
							fclose(fp);
							return 1;
						}
					}
					command = NONE;
					break;
				case MAXMUTATIONS:
					{
						int val = atoi(str);
						if (val < 0) {
							fprintf(stderr, "maxmutations must be >= 0: %s\n", str);
							fclose(fp);
							return 1;
						}
						pop->max_mutations = val;
					}
					command = NONE;
					break;
				case CROSSOVERPROB:
					{
						float val = atof(str);
						if (val < 0 || val > 1) {
							fprintf(stderr,
								"crossoverprob must be between [0,1]: %s\n",
								str);
							fclose(fp);
							return 1;
						}
						pop->crossover_prob = val;
					}
					command = NONE;
					break;
				case KEEPRATIO:
					{
						float val = atof(str);
						if (val <= 0 || val > 1.0) {
							fprintf(stderr,
								"keepratio must be > 0.0 and <= 1.0: %s\n",
								str);
							fclose(fp);
							return 1;
						}
						pop->keep_ratio = val;
					}
					command = NONE;
					break;
				case BREEDRATIO:
					{
						float val = atof(str);
						if (val <= 0 || val > 1.0) {
							fprintf(stderr,
								"breedratio must be > 0.0 and <= 1.0: %s\n",
								str);
							fclose(fp);
							return 1;
						}
						pop->breed_ratio = val;
					}
					command = NONE;
					break;
				default:
					fprintf(stderr, "Unrecognised command: %d\n", command);
					fclose(fp);
					return 1;
				}
			}
			pos = 0;
		} else {
			pos++;
			if (pos >= BUF_SIZE) {
				fprintf(stderr,
					"File: %s contains strings >= %d characters\n",
					filename, BUF_SIZE);
				fclose(fp);
				return 1;
			}
		}
	}

	/* validate */
	if (pop->keep_ratio + pop->breed_ratio > 1.0) {
		fprintf(stderr,
			"keepratio + breed ratio must be > 0.0 and <= 1.0 (%g + %g = %g)\n",
			pop->keep_ratio, pop->breed_ratio, pop->keep_ratio + pop->breed_ratio);
		fclose(fp);
		return 1;
	}

	fclose(fp);
	return 0;
}
